/*-//GEN-BEGIN:copyright
 * ForBILE - Framework for Building Interactive Learning Environments
 * Computing Institute - Federal University of Alagoas - Brazil
 * Group of Otimization of the Web
 *
 * Copyright (C) 2007 Ig Bittencourt (ig.ibert@gmail.com)
 *		      Camila Nunes (camilapbnunes@hotmail.com)
 *	              Marcos Tadeu (zero.ufal@gmail.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *///GEN-END:copyright
package br.ufal.ic.forbile.agents.controller;

import br.ufal.ic.forbile.ForBILEFacade;
import br.ufal.ic.forbile.agents.ForBILEAgent;
import br.ufal.ic.forbile.agents.behaviours.GeneralActuator;
import br.ufal.ic.forbile.agents.behaviours.RegisterServices;
import br.ufal.ic.forbile.agents.controller.behaviours.SensorController;
import br.ufal.ic.forbile.agents.mediator.MediatorAgent;
import br.ufal.ic.forbile.agents.persistence.PersistenceEMATHEMAAgent;
import br.ufal.ic.forbile.agents.persistence.PersistenceOWLAgentInteraction;
import br.ufal.ic.forbile.exceptions.ForBILEException;
import br.ufal.ic.forbile.log.FacadeLog;
import br.ufal.ic.forbile.log.PersistenceOWLLog;
import br.ufal.ic.utils.Constants;
import br.ufal.ic.utils.RegionServer;
import br.ufal.ic.utils.SharedMem;
import jade.core.AID;
import jade.core.Profile;
import jade.core.ProfileImpl;
import jade.lang.acl.ACLMessage;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import jade.wrapper.ControllerException;
import jade.wrapper.StaleProxyException;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * ControllerAgent2.java
 *
 * <p>A full description of this class.
 *
 * @see SomeRelatedClass.
 *
 * @author <a href="mailto:camilapbnunes@hotmail.com">camila</a>.
 * @since 0.1
 * @version 0.1
 *
 */
public class ControllerAgent extends ForBILEAgent {
    
    // Constants -----------------------------------------------------
    
    // Attributes ----------------------------------------------------
    
    private List<ForBILEAgent> agents;
    private ConfigureAgent configureAgent;
    private enum States {
        STARTENVIRONMENT,
        ADDAGENT,
        ADDAGENTSOCIETY,
        REMOVEAGENT,
        REMOVEAGENTSOCIETY,
        UPDATEAGENT,
        UPDATEAGENTSOCIETY
    };
    private States actualState;
    private ForBILEFacade forBILEFacade;
    protected jade.core.Runtime runtime;
    
    // Static --------------------------------------------------------
    
    private static Logger logger;
    
    static {
        logger = Logger.getLogger(ControllerAgent.class.getName());
    }
    
    // Constructors --------------------------------------------------
    /**
     * Creates a new instance of ControllerAgent
     * @param containerName
     * @param agentLocalName
     */
    public ControllerAgent(final String containerName, final String agentLocalName) {
        super(containerName, agentLocalName);
        
        try {
            RegionServer server = new RegionServer();
            Thread.sleep(7000);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        this.actualState = States.STARTENVIRONMENT;
        this.runtime = jade.core.Runtime.instance();
        this.configureAgent = new ConfigureAgent(this.runtime);
        this.agents = new ArrayList<ForBILEAgent>();
        //this.forBILEFacade = forBILEFacade;
    }
    
    // Public --------------------------------------------------------
    
    public void agentAdded(final AID aIDAgent, final boolean agentAdded) throws ForBILEException{
        if (agentAdded) {
            for (ForBILEAgent agent : this.agents) {
                if (agent.getAID().getLocalName().equals(aIDAgent.getLocalName())) {
                    this.agents.remove(agent);
                    System.out.println("Agent " + aIDAgent.getLocalName() + " added in the environment!");
                    System.out.println("this.actualState: " + this.actualState);
                    if (this.actualState.toString().equalsIgnoreCase("STARTENVIRONMENT")) {
                        startForBILEEnvironment();
                    }
                    if (this.actualState.toString().equalsIgnoreCase("ADDAGENTSOCIETY")) {
                        addAgentSociety();
                    }
                    break;
                }
            }
        } else {
            System.out.println("Agent " + aIDAgent + " not added!");
        }
    }
    
    /**
     *
     * @param subject
     * @param predicate
     * @param object
     */
    public void requireRetrieve(final String subject, final String predicate, final String object) {
        PersistenceOWLLog persistenceOWLLog = new PersistenceOWLLog();
        persistenceOWLLog.setSubject(subject);
        persistenceOWLLog.setPredicate(predicate);
        persistenceOWLLog.setObject(object);
        persistenceOWLLog.setRetrieveKey("Map");
        persistenceOWLLog.setOwlFile("emathema");
        persistenceOWLLog.setMethod("RETRIEVE");
        try {
            SharedMem mem = (SharedMem) Naming.lookup(RegionServer.HOST_URL);
            mem.getRegion(Constants.OWLPersistence).write(persistenceOWLLog);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    /**
     * It is solicited after a retrieve requisition.
     * It gets the PersistenceLog.
     */
    public void requestRetrieve() {
        PersistenceOWLLog persistenceLogAux = new PersistenceOWLLog();
        try {
            SharedMem mem = (SharedMem) Naming.lookup(RegionServer.HOST_URL);
            persistenceLogAux = (PersistenceOWLLog) mem.getRegion(Constants.OWLPersistence).retrieveIfExist(persistenceLogAux);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public void addAgent(final ForBILEAgent agent) throws ForBILEException  {
        this.configureAgent.starAgent(agent);
    }
    
    public void addAgentSociety(final List<ForBILEAgent> agents) throws ForBILEException {
        this.agents = agents;
        this.actualState = States.ADDAGENTSOCIETY;
        System.out.println("addAgentSociety(final List<ForBILEAgent> agents): " + agents);
        addAgentSociety();
    }
    
    public void executeFacadeService(FacadeLog facadeLog) {
        this.forBILEFacade = ForBILEFacade.getInstance();
        facadeLog.setContent("executeFacadeService");
        this.addBehaviour(new GeneralActuator(super.getMediatorAgent(), facadeLog));
    }
    
    public void a2oCommunicationReturn(final Object object){
        this.forBILEFacade.setA2oCommunicationReturned(true, object);
    }
    
// Z implementation ----------------------------------------------
    
// Y overrides ---------------------------------------------------
    protected void setup() {
        List<String> habilities = new ArrayList<String>();
        habilities.add("addAgent");
        habilities.add("addAgentSociety");
        super.serviceHabilities.put("CONTROL", habilities);
        super.agentServicesHabilities.put(this, super.serviceHabilities);
        addBehaviour(new RegisterServices(this));
        sensor();
        this.agents.add(new PersistenceOWLAgentInteraction("ForBILE-PersistenceContainer", "PersistenceOWLAgentInteraction"));
        this.agents.add(new PersistenceEMATHEMAAgent("ForBILE-PersistenceContainer", "PersistenceEMATHEMAAgent"));
        this.agents.add(new MediatorAgent("ForBILE-Main-Container", "MediatorAgent"));
        try {
            startForBILEEnvironment();
        } catch (ForBILEException ex) {
            ex.printStackTrace();
        }
        setEnabledO2ACommunication(true, 100);
    }

// Package protected ---------------------------------------------
    
// Protected -----------------------------------------------------
    
    protected void sensor() {
        addBehaviour(new SensorController(this, 2000));
    }
    
// Private -------------------------------------------------------
    private void startForBILEEnvironment() throws ForBILEException {
        System.out.println("startForBILEEnvironment()");
        
        if (this.agents.isEmpty()) {
//            ACLMessage message = new ACLMessage(ACLMessage.INFORM);
//            message.setProtocol("CreateSATA");
//            System.out.println("message.setProtocol(CreateSATA);");
//            addBehaviour(new GeneralActuator(super.getMediatorAgent(), message));
           // this.forBILEFacade.setEnvironmentStarted(true);
        }
        
        for (ForBILEAgent agent : this.agents) {
            this.addAgent(agent);
            break;
        }
    }
    
    private void addAgentSociety() throws ForBILEException{
        System.out.println("addAgentSociety");
        for (ForBILEAgent agent : this.agents) {
            System.out.println("ForBILEAgent agent : this.agents " + agent);
            this.addAgent(agent);
            break;
        }
    }
    
    protected void takeDown() {
        setEnabledO2ACommunication(false, 0);
    }
// Inner classes -------------------------------------------------
    
    class ConfigureAgent {
        
        protected jade.core.Runtime runtime;
        private List<AgentContainer> agentContainer;
        
        public ConfigureAgent(jade.core.Runtime runtime) {
            this.runtime = runtime;
            this.agentContainer = new ArrayList<AgentContainer>();
        }
        
        private void starAgent(ForBILEAgent agent) throws ForBILEException {
            AgentContainer agentContainer = createContainer(agent.getContainerName());
            try {
                addAgent(agentContainer, agent);
            } catch (StaleProxyException ex) {
                throw new ForBILEException("Agent could not be added");
            }
        }
        
        private void addAgent(AgentContainer container, ForBILEAgent agent) throws StaleProxyException {
            AgentController controller  = container.acceptNewAgent(agent.getAgentLocalName(), agent);
            controller.start();
        }
        
        private void removeAgent() {
            
        }
        
        private void removeAgentSociety() {
            
        }
        
        private void updateAgent() {
            
        }
        
        private void updateAgentSociety() {
            
        }
        
        
        private AgentContainer createContainer(String containerName) {
            Profile profile = new ProfileImpl();
            profile.setParameter(profile.CONTAINER_NAME, containerName);
            
            AgentContainer container = null;
            
            for (AgentContainer ac: agentContainer) {
                try {
                    if (ac.getContainerName().equals(containerName)) {
                        container = ac;
                    }
                } catch (ControllerException ex) {
                    ex.printStackTrace();
                }
            }
            
            if (container == null) {
                container = this.runtime.createAgentContainer(profile);
                agentContainer.add(container);
            }
            
            return container;
        }
    }
}
